package com.example.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.common.Interface.order.entity.dto.*;
import com.example.common.Interface.order.entity.vo.OrderVO;
import com.example.common.Interface.order.service.OrderService;
import com.example.common.Interface.product.service.ProductService;
import com.example.common.response.BizException;
import com.example.common.util.lock.RedisLockUtils;
import com.example.order.converter.OrderConverter;
import com.example.order.dao.mapper.OrderInfoMapper;
import com.example.order.dao.mapper.OrderMapper;
import com.example.order.dao.po.Order;
import com.example.order.dao.po.OrderInfo;
import com.example.order.enums.PaymentStatus;
import com.example.order.utility.EncryptOrderId;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.util.*;

@DubboService
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Resource
    private OrderMapper orderMapper;

    @Resource
    private OrderInfoMapper orderInfoMapper;

    @DubboReference(check = false)
    private ProductService productService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedisLockUtils redisLock;
    private final Cache<String,Order> orderCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofMinutes(5)).maximumSize(1000).build();
    private final Cache<String, Map<String, Order>> orderListCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofMinutes(5)).maximumSize(1000).build();
    private final Cache<String, Map<String, OrderInfo>> orderInfoListCache = Caffeine.newBuilder().expireAfterWrite(Duration.ofMinutes(5)).maximumSize(1000).build();

    private static final String ORDER_ID_KEY_PREFIX = "order:id:";
    private static final String ORDER_USER_ID_KEY_PREFIX = "order:userId:";
    private static final String ORDERINFO_ORDER_ID_KEY_PREFIX = "orderInfo:orderId:";
    private static final String LOCK_KEY_PREFIX = "lock:";
    private static final String MARKORDERPAID_PREFIX = "markOrderPaid:";

    private static final String PLACE_ORDER_LOCK_KEY_PREFIX = "placeOrder:";

    public Order getOrderByOrderId(Long orderId) {
        String key = ORDER_ID_KEY_PREFIX + orderId;
        var order = orderCache.getIfPresent(key);
        if (order != null) {
            return orderCache.getIfPresent(key);
        }
        var orderJson = redisTemplate.opsForValue().get(key);
        if(orderJson != null){
            order = (Order) orderJson;
            orderCache.put(key, order);
            return order;
        }
        var value = Math.random();
        boolean getLock = this.redisLock.getLock(LOCK_KEY_PREFIX + key, value);
        if(getLock){
            log.info("{}:成功获取[{}]锁",Thread.currentThread().getName(), key);
            order = orderMapper.selectById(orderId);
            if(order == null){
                redisLock.releaseLock(key, value);
                log.info("{}:释放[{}]锁",Thread.currentThread().getName(),key);
                return null;
            }
            redisTemplate.opsForValue().set(key, order, Duration.ofMinutes(5));
            orderCache.put(key, order);
            redisLock.releaseLock(LOCK_KEY_PREFIX + key,value);
            log.info("{}:释放[{}]锁",Thread.currentThread().getName(),key);
            return order;
        }
        return null;
    }

    public List<Order> getOrderBuUserId(Long userId){
        String key = ORDER_USER_ID_KEY_PREFIX + userId;
        var orders = orderListCache.getIfPresent(key);
        if (orders != null) {
            return orders.values().stream().toList();
        }
        var entries = redisTemplate.opsForHash().entries(key);
        if(orders != null){
            Map<String, Order> orderMap = new HashMap<>();
            for (Map.Entry<Object, Object> entry : entries.entrySet()) {
                if (entry.getKey() instanceof String kkey && entry.getValue() instanceof Order value) {
                    orderMap.put(kkey, value);
                } else {
                    log.warn("缓存数据格式异常 key:{} value:{}",
                            entry.getKey().getClass(),
                            entry.getValue().getClass());
                }
            }
            orderListCache.put(key, orderMap);
            return orderMap.values().stream().toList();
        }
        var value = Math.random();
        boolean getLock = this.redisLock.getLock(LOCK_KEY_PREFIX + key, value);
        if(getLock){
            log.info("{}:成功获取[{}]锁",Thread.currentThread().getName(), key);
            LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Order::getUserId, userId);
            var orderList = orderMapper.selectList(queryWrapper);
            if(orders == null){
                redisLock.releaseLock(key, value);
                log.info("{}:释放[{}]锁",Thread.currentThread().getName(),key);
                return null;
            }
            Map<String, Order> orderMap = new HashMap<>();
            for(var order : orderList){
                orderMap.put(ORDER_ID_KEY_PREFIX + order.getId(), order);
            }
            redisTemplate.opsForHash().putAll(key, orderMap);
            orderListCache.put(key, orderMap);
            redisLock.releaseLock(LOCK_KEY_PREFIX + key,value);
            log.info("{}:释放[{}]锁",Thread.currentThread().getName(),key);
            return orderList;
        }
        return null;
    }

    public List<OrderInfo> getOrderInfoByOrderId(Long orderId) {
        String key = ORDERINFO_ORDER_ID_KEY_PREFIX + orderId;
        var orders = orderInfoListCache.getIfPresent(key);
        if (orders != null) {
            return orders.values().stream().toList();
        }
        var entries = redisTemplate.opsForHash().entries(key);
        if(entries != null){
            Map<String, OrderInfo> orderMap = new HashMap<>();
            for (Map.Entry<Object, Object> entry : entries.entrySet()) {
                if (entry.getKey() instanceof String kkey && entry.getValue() instanceof OrderInfo value) {
                    orderMap.put(kkey, value);
                } else {
                    log.warn("缓存数据格式异常 key:{} value:{}",
                            entry.getKey().getClass(),
                            entry.getValue().getClass());
                }
            }
            orderInfoListCache.put(key, orderMap);
            return orderMap.values().stream().toList();
        }
        var value = Math.random();
        boolean getLock = this.redisLock.getLock(LOCK_KEY_PREFIX + key, value);
        if(getLock){
            log.info("{}:成功获取[{}]锁",Thread.currentThread().getName(), key);
            LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(OrderInfo::getOrderId, orderId);
            var orderList = orderInfoMapper.selectList(queryWrapper);
            if(orders == null){
                redisLock.releaseLock(key, value);
                log.info("{}:释放[{}]锁",Thread.currentThread().getName(),key);
                return null;
            }
            Map<String, OrderInfo> orderMap = new HashMap<>();
            for(var order : orderList){
                orderMap.put(ORDER_ID_KEY_PREFIX + order.getId(), order);
            }
            redisTemplate.opsForHash().putAll(key, orderMap);
            orderInfoListCache.put(key, orderMap);
            redisLock.releaseLock(LOCK_KEY_PREFIX + key,value);
            log.info("{}:释放[{}]锁",Thread.currentThread().getName(),key);
            return orderList;
        }
        return null;
    }

    public boolean deleteOrder(Long orderId) {
        Order order = getOrderByOrderId(orderId);
        if(order == null){
            return false;
        }
        if(order.getPaymentStatus() != PaymentStatus.PENDING){
            return false;
        }
        if(orderMapper.deleteById(orderId) != 1){
            return false;
        }
        LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OrderInfo::getOrderId, orderId);
        return orderInfoMapper.delete(queryWrapper) > 0;
    }

    public boolean updateOrder(Order order) {
        String orderIdKey = ORDER_ID_KEY_PREFIX + order.getId();
        String orderUserIdKey = ORDER_USER_ID_KEY_PREFIX + order.getUserId();
        redisTemplate.delete(orderIdKey);
        orderCache.invalidate(orderIdKey);
        var value = Math.random();
        boolean getLock = redisLock.getLock(LOCK_KEY_PREFIX + orderIdKey, value);
        if(getLock){
            log.info("{}:成功获取[{}]锁",Thread.currentThread().getName(), orderIdKey);
            if(orderMapper.updateById(order) != 1){
                log.info("{}:释放[{}]锁",Thread.currentThread().getName(),orderIdKey);
                redisLock.releaseLock(orderIdKey, value);
                return false;
            }
            redisTemplate.opsForHash().put(orderUserIdKey, order.getId(), order);
            orderListCache.invalidate(orderUserIdKey);
        }
        return true;
    }

    public boolean updateOrderInfo(OrderInfo orderInfo) {
        String orderInfoOrderIdKey = ORDERINFO_ORDER_ID_KEY_PREFIX + orderInfo.getOrderId();
        var value = Math.random();
        boolean getLock = redisLock.getLock(LOCK_KEY_PREFIX + orderInfoOrderIdKey, value);
        if(getLock){
            log.info("{}:成功获取[{}]锁",Thread.currentThread().getName(), orderInfoOrderIdKey);
            if(orderInfoMapper.updateById(orderInfo)!= 1){
                log.info("{}:释放[{}]锁",Thread.currentThread().getName(),orderInfoOrderIdKey);
                redisLock.releaseLock(orderInfoOrderIdKey, value);
                return false;
            }
            redisTemplate.opsForHash().put(orderInfoOrderIdKey, orderInfo.getId(), orderInfo);
            orderInfoListCache.invalidate(orderInfoOrderIdKey);
            log.info("{}:释放[{}]锁",Thread.currentThread().getName(),orderInfoOrderIdKey);
        }
        return true;
    }

    @Override
    @Transactional
    public PlaceOrderRespDTO placeOrder(PlaceOrderReqDTO request) {
        log.info("PlaceOrderReq rpc 服务被调用");
        Long userId = request.getUserId();
        if (userId == null || userId <= 0) {
            log.warn("userId不合法");
            throw new BizException(100, "userId不合法");
        }
        String userCurrency = request.getUserCurrency();
        if (! isValidUserCurrency(userCurrency)) {
            log.warn("userCurrency不合法");
            throw new BizException(100, "userCurrency不合法");
        }
        String address = request.getAddressVO().serialize();
        if(! isValidAddress(address)){
            log.warn("address不合法");
            throw new BizException(100, "address不合法");
        }
        String email = request.getEmail();
        if (! isValidEmail(email)) {
            log.warn("email不合法");
            throw new BizException(100, "email不合法");
        }
        var orderItems = request.getOrderItems();
        if (orderItems.isEmpty()) {
            log.warn("orderItems为空");
            throw new BizException(100, "orderItems为空");
        }
        Date now = new Date();
        Order order = Order.builder()
                .userId(userId)
                .userCurrency(userCurrency)
                .email(email)
                .createdAt(now.getTime())
                .address(address)
                .cost(orderItems.stream().map(OrderItemDTO::getCost).reduce(Long::sum).get())
                .paymentStatus(PaymentStatus.PENDING)
                .build();

        String key = PLACE_ORDER_LOCK_KEY_PREFIX + request.getUserId();
        var value = Math.random();
        boolean getLock = redisLock.getLock(key, value);
        String encryptOrderId = null;
        if(getLock){
            log.info("{}:成功获取[{}]锁",Thread.currentThread().getName(), key);
            try{
                int nums = orderMapper.insert(order);
                if (nums <= 0) {
                    log.info("订单插入失败");
                    throw new RuntimeException("订单插入失败");
                }

                for(var orderItem : orderItems){
                    var cartItem = orderItem.getCartItemVO();
                    Long productId = cartItem.getProductId();
                    Long quantity = cartItem.getQuantity();
                    Long cost = orderItem.getCost();
                    if (productId == null || quantity == null || cost == null || productId <= 0 || quantity <= 0 || cost < 0) {
                        log.warn("orderItems不合法");
                        throw new RuntimeException("orderItems不合法");
                    }
                    if(orderInfoMapper.insert(OrderInfo.builder()
                            .orderId(order.getId())
                            .productId(productId)
                            .cost(cost)
                            .quantity(quantity).build()) != 1){

                        log.warn("订单详情插入失败");
                        throw new RuntimeException("订单详情插入失败");
                    }
                }
                var resp = productService.deductStock(orderItems.stream().map(OrderItemDTO::getCartItemVO).toList());
                if(! resp){
                    log.warn("扣减库存失败");
                    throw new RuntimeException("扣减库存失败");
                }
            }finally {
                redisLock.releaseLock(key, value);
                log.info("{}:释放[{}]锁",Thread.currentThread().getName(),key);
            }
            encryptOrderId = EncryptOrderId.encryptOrderId(order.getId());
        }
        return PlaceOrderRespDTO.builder()
                .orderResult(OrderResult.builder().orderId(encryptOrderId).build()).build();

    }

    @Override
    public ListOrderRespDTO listOrder(ListOrderReqDTO request) {
        log.info("listOrder rpc 服务被调用");
        Long userId = request.getUserId();
        if (userId <= 0) {
            log.warn("userId不合法");
            throw new BizException(100, "userId不合法");
        }
        var orders = getOrderBuUserId(userId);
        if(orders == null){
            log.warn("订单列表为空");
            throw new BizException(100, "订单列表为空");
        }
        var resp = new ArrayList<OrderVO>();
        for(var order : orders){
            var orderItems = getOrderInfoByOrderId(order.getId());
            if(orderItems == null){
                log.warn("订单详情列表为空");
                throw new BizException(100, "订单详情列表为空");
            }
            var orderVO = OrderConverter.toOrderVO(order);
            resp.add(orderVO);
        }
        return ListOrderRespDTO.builder()
               .orders(resp).build();
    }

    @Override
    public MarkOrderPaidRespDTO markOrderPaid(MarkOrderPaidReqDTO request) {
        log.info("MarkOrderPaidReq rpc 服务被调用");
        Long orderId = EncryptOrderId.decryptOrderId(request.getOrderId());
        var order = getOrderByOrderId(orderId);
        if(order == null){
            log.warn("订单不存在");
            throw new BizException(100, "订单不存在");
        }
        var value = Math.random();
        String key = LOCK_KEY_PREFIX + MARKORDERPAID_PREFIX + request.getUserId();
        boolean getLock = redisLock.getLock(key, value);
        if(getLock){
            log.info("{}:成功获取[{}]锁",Thread.currentThread().getName(), ORDER_ID_KEY_PREFIX + orderId);
            if(order.getPaymentStatus() == PaymentStatus.PENDING){
                order.setPaymentStatus(PaymentStatus.SUCCESS);
            }
            if(! updateOrder(order)){
                log.warn("订单状态更新失败");
                redisLock.releaseLock(key, value);
                log.info("{}:释放[{}]锁",Thread.currentThread().getName(),ORDER_ID_KEY_PREFIX + orderId);
                throw new BizException(100, "订单状态更新失败");
            }
            redisLock.releaseLock(key, value);
            log.info("{}:释放[{}]锁",Thread.currentThread().getName(),ORDER_ID_KEY_PREFIX + orderId);
        }
        return MarkOrderPaidRespDTO.builder().build();
    }

    @Override
    public IsValidOrderIdRespDTO isValidOrderId(IsValidOrderIdReqDTO request) {
        log.info("IsValidOrderIdReq rpc 服务被调用");
        if(request.getOrderId() == null){
            log.warn("IsValidOrderIdReq rpc 订单号为空");
            throw new BizException(100, "订单号为空");
        }
        return IsValidOrderIdRespDTO.builder().isValid(true).build();
//        return IsValidOrderIdRespDTO.builder().isValid(EncryptOrderId.isValidOrderId(request.getOrderId())).build();
    }

    public Boolean isValidUserCurrency(String userCurrency) {
        // todo
        return true;
    }

    private boolean isValidAddress(String address) {
        // todo
        return true;
    }

    private boolean isValidEmail(String email) {
        // todo
        return true;
    }
}
